#ifndef GLPR2_TYPES_H
#define GLPR2_TYPES_H

#include "../log.h"

#include "../log.h"
#include <math.h>
#include "../types.h"

namespace G3 {

// 2 dimensional vectors
template <class T>
class VEC2 {
	public:
		VEC2 (T X = 0, T Y = 0);

		T x, y;

		T& operator[] (const int &id);

		VEC2<T>& operator= (const T &t);
		VEC2<T>& operator= (const VEC2 <T> &v);

		bool operator== (const VEC2 <T> &v) const;
		bool operator!= (const VEC2 <T> &v) const;
		bool operator< (const VEC2 <T> &v) const;

		VEC2<T> operator+ (const T &t) const;
		VEC2<T> operator+ (const VEC2 <T> &v) const;

		VEC2<T> operator- (void) const;

		VEC2<T> operator- (const T &t) const;
		VEC2<T> operator- (const VEC2 <T> &v) const;

		VEC2<T> operator* (const T &t) const;
		T operator* (const VEC2 <T> &v) const;

		VEC2<T> operator/ (const T &t) const;

		VEC2<T>& operator+= (const T &t);
		VEC2<T>& operator+= (const VEC2 <T> &v);
		VEC2<T>& operator-= (const T &t);
		VEC2<T>& operator-= (const VEC2 <T> &v);
		VEC2<T>& operator*= (const T &t);
		VEC2<T>& operator/= (const T &t);

		VEC2<T>& Reset ();

		void SetCoord (const int &id, T val);
		T GetCoord (const int &id) const;

		double GetLength () const;
		T GetRootLength () const;
		T GetL1Norm () const;

		void Normalize ();

		void SwapAxis ();

		VEC2<T> GetPerpendicular ();
};

// 3 dimensional vectors
template <class T>
class VEC3 {
	public:
		VEC3 (T X = 0, T Y = 0, T Z = 0);

		T x, y, z;

		T& operator[] (const int &id);

		VEC3<T>& operator= (const T &t);
		VEC3<T>& operator= (const VEC3 <T> &v);

		bool operator== (const VEC3 <T> &v) const;
		bool operator!= (const VEC3 <T> &v) const;
		bool operator< (const VEC3 <T> &v) const;

		VEC3<T> operator+ (const T &t) const;
		VEC3<T> operator+ (const VEC3 <T> &v) const;

		VEC3<T> operator- (void) const;

		VEC3<T> operator- (const T &t) const;
		VEC3<T> operator- (const VEC3 <T> &v) const;

		VEC3<T> operator* (const T &t) const;
		T operator* (const VEC3 <T> &v) const;

		VEC3<T> operator/ (const T &t) const;

		VEC3<T>& operator+= (const T &t);
		VEC3<T>& operator+= (const VEC3 <T> &v);
		VEC3<T>& operator-= (const T &t);
		VEC3<T>& operator-= (const VEC3 <T> &v);
		VEC3<T>& operator*= (const T &t);
		VEC3<T>& operator/= (const T &t);

		VEC3<T>& Reset ();

		void SetCoord (const int &id, T val);
		T GetCoord (const int &id) const;

		double GetLength () const;
		T GetRootLength () const;
		T GetL1Norm () const;

		VEC3<T> GetSlopeAngle (double *Angle);

		void Normalize ();
};

//==============================================================================
// The crossproduct of vectors
//==============================================================================
template <class T>
T CrossProduct (const VEC2<T> &A, const VEC2<T> &B);
template <class T>
T CrossProduct (const VEC2<T> &A, const VEC2<T> &B, const VEC2<T> &C);
template <class T>
VEC3<T> CrossProduct (const VEC3<T> &A, const VEC3<T> &B);

//==============================================================================
// Finds angle between vectors
//==============================================================================
template <class T>
double GetAngle (const VEC3<T> &A, const VEC3<T> &B);

//==============================================================================
// Projects vector A onto vector B
//==============================================================================
template <class T>
VEC3<T> ProjectVector (const VEC3<T> &A, const VEC3<T> &B);

//==============================================================================
// Projects a point onto a line between LineA and LineB
//==============================================================================
template <class T>
VEC3<T> ProjectPointOnLine (const VEC3<T> &Point, const VEC3<T> &LineA, const VEC3<T> &LineB);

//==============================================================================
// Calculates polygon normal
//==============================================================================
template <class T>
VEC3<T> GetNormal (const VEC3<T> &A, const VEC3<T> &B, const VEC3<T> &C);

//==============================================================================
// Just finds the distance between 2 points
//==============================================================================
template <class T>
double GetDistance (const VEC3<T> &A, const VEC3<T> &B);

//==============================================================================
// Transforms point P into triangle ABC barycentric coordinates
//==============================================================================
template <class T>
VEC3<T> Barycentric (const VEC3<T> &A, const VEC3<T> &B, const VEC3<T> &C, const VEC3<T> &P);

//==============================================================================
// Calculates the area of a 2D triangle
//==============================================================================
template <class T>
T TriArea2 (const VEC2<T> &A, const VEC2<T> &B, const VEC2<T> &C);

//==============================================================================
// Returns true if the quad is convex
//==============================================================================
template <class T>
bool IsConvexQuad (const VEC3<T> &A, const VEC3<T> &B, const VEC3<T> &C, const VEC3<T> &D);

//==============================================================================
// Gets a random 2D unit vector
//==============================================================================
template <class T>
VEC2<T> GetRandVector2 ();

//==============================================================================
// Gets a random 3D unit vector
//==============================================================================
template <class T>
VEC3<T> GetRandVector3 ();

//==============================================================================
// Casts a 2D vector to another type
//==============================================================================
template <class T, class W>
VEC2<W> CastVector2 (const VEC2<T> &v);

//==============================================================================
// Casts a 3D vector to another type
//==============================================================================
template <class T, class W>
VEC3<W> CastVector3 (const VEC3<T> &v);

//==============================================================================
// Explicit template instantiation
//==============================================================================
extern template class VEC2<int>;
extern template class VEC2<float>;
extern template class VEC2<double>;

extern template class VEC3<int>;
extern template class VEC3<float>;
extern template class VEC3<double>;

//==============================================================================
// The crossproduct of vectors A and B
//==============================================================================
extern template int CrossProduct (const VEC2<int> &A, const VEC2<int> &B);
extern template float CrossProduct (const VEC2<float> &A, const VEC2<float> &B);
extern template double CrossProduct (const VEC2<double> &A, const VEC2<double> &B);

extern template int CrossProduct (const VEC2<int> &A, const VEC2<int> &B, const VEC2<int> &C);
extern template float CrossProduct (const VEC2<float> &A, const VEC2<float> &B, const VEC2<float> &C);
extern template double CrossProduct (const VEC2<double> &A, const VEC2<double> &B, const VEC2<double> &C);

extern template VEC3<int> CrossProduct (const VEC3<int> &A, const VEC3<int> &B);
extern template VEC3<float> CrossProduct (const VEC3<float> &A, const VEC3<float> &B);
extern template VEC3<double> CrossProduct (const VEC3<double> &A, const VEC3<double> &B);

extern template double GetAngle (const VEC3<int> &A, const VEC3<int> &B);
extern template double GetAngle (const VEC3<float> &A, const VEC3<float> &B);
extern template double GetAngle (const VEC3<double> &A, const VEC3<double> &B);

extern template VEC3<int> ProjectVector (const VEC3<int> &A, const VEC3<int> &B);
extern template VEC3<float> ProjectVector (const VEC3<float> &A, const VEC3<float> &B);
extern template VEC3<double> ProjectVector (const VEC3<double> &A, const VEC3<double> &B);

extern template VEC3<int> ProjectPointOnLine (const VEC3<int> &Point, const VEC3<int> &LineA, const VEC3<int> &LineB);
extern template VEC3<float> ProjectPointOnLine (const VEC3<float> &Point, const VEC3<float> &LineA, const VEC3<float> &LineB);
extern template VEC3<double> ProjectPointOnLine (const VEC3<double> &Point, const VEC3<double> &LineA, const VEC3<double> &LineB);

extern template VEC3<int> GetNormal (const VEC3<int> &A, const VEC3<int> &B, const VEC3<int> &C);
extern template VEC3<float> GetNormal (const VEC3<float> &A, const VEC3<float> &B, const VEC3<float> &C);
extern template VEC3<double> GetNormal (const VEC3<double> &A, const VEC3<double> &B, const VEC3<double> &C);

extern template double GetDistance (const VEC3<int> &A, const VEC3<int> &B);
extern template double GetDistance (const VEC3<float> &A, const VEC3<float> &B);
extern template double GetDistance (const VEC3<double> &A, const VEC3<double> &B);

extern template VEC3<int> Barycentric (const VEC3<int> &A, const VEC3<int> &B, const VEC3<int> &C, const VEC3<int> &P);
extern template VEC3<float> Barycentric (const VEC3<float> &A, const VEC3<float> &B, const VEC3<float> &C, const VEC3<float> &P);
extern template VEC3<double> Barycentric (const VEC3<double> &A, const VEC3<double> &B, const VEC3<double> &C, const VEC3<double> &P);

extern template int TriArea2 (const VEC2<int> &A, const VEC2<int> &B, const VEC2<int> &C);
extern template float TriArea2 (const VEC2<float> &A, const VEC2<float> &B, const VEC2<float> &C);
extern template double TriArea2 (const VEC2<double> &A, const VEC2<double> &B, const VEC2<double> &C);

extern template bool IsConvexQuad (const VEC3<int> &A, const VEC3<int> &B, const VEC3<int> &C, const VEC3<int> &D);
extern template bool IsConvexQuad (const VEC3<float> &A, const VEC3<float> &B, const VEC3<float> &C, const VEC3<float> &D);
extern template bool IsConvexQuad (const VEC3<double> &A, const VEC3<double> &B, const VEC3<double> &C, const VEC3<double> &D);

extern template VEC2<int> GetRandVector2 ();
extern template VEC2<float> GetRandVector2 ();
extern template VEC2<double> GetRandVector2 ();

extern template VEC3<int> GetRandVector3 ();
extern template VEC3<float> GetRandVector3 ();
extern template VEC3<double> GetRandVector3 ();

extern template VEC2<int> CastVector2 (const VEC2<float> &v);
extern template VEC2<int> CastVector2 (const VEC2<double> &v);
extern template VEC2<float> CastVector2 (const VEC2<int> &v);
extern template VEC2<float> CastVector2 (const VEC2<double> &v);
extern template VEC2<double> CastVector2 (const VEC2<int> &v);
extern template VEC2<double> CastVector2 (const VEC2<float> &v);

extern template VEC3<int> CastVector3 (const VEC3<float> &v);
extern template VEC3<int> CastVector3 (const VEC3<double> &v);
extern template VEC3<float> CastVector3 (const VEC3<int> &v);
extern template VEC3<float> CastVector3 (const VEC3<double> &v);
extern template VEC3<double> CastVector3 (const VEC3<int> &v);
extern template VEC3<double> CastVector3 (const VEC3<float> &v);

//==============================================================================
// Types
//==============================================================================
typedef VEC2<int>		VEC2I;
typedef VEC2<float>		VEC2F;
typedef VEC2<double>	VEC2D;
typedef VEC3<int>		VEC3I;
typedef VEC3<float>		VEC3F;
typedef VEC3<double>	VEC3D;

}

#endif
